////////////////////////////////////////////////////////
// CodePlex - metacpp
//--------------------
// This file is part of "metacpp" project on CodePlex. The
// goal if the project is to demonstrate basic techniques
// used in C++ metaprogrammins.
// For more info and comments please visit:
// http://www.codeplex.com/metacpp/
////////////////////////////////////////////////////////

#pragma once;

#include <iostream>
#include <typeinfo.h>

#include "typelists.hpp"


namespace metacpp {


/**
 * variant
 *
 * implements "union" concept, but supports non-POD
 * types too.
 * <used as a demo only, for real applications use boost::vatiant>
 *
 * Usage:
 *
 * typedef variant<TYPELIST_4(int, char, std::string, double)> my_variant;
 * my_variant v1(10);
 * my_variant v2(std::string("hello"));
 *
 * std::cout << v1 << ", " << v2 << std::endl;
 * int i = v1.get<int>;
 *
 * The variant adds 4 bytes (a pointer) to a size of max type,
 * and each access to inner objects const a virtual function
 * invocation (there's a solution which eliminates the virtual part)
 *
 * Topics totally out of scope:
 * (1) Alignment of different types
 * (2) Swapping (required by some STL algorithms)
 * (3) Visitor implementation (see AbstractVisitor for dynamic version hint)
 * (4) Accepting tpes convertible to one of storage types
 */
template<typename TLIST>
class variant {
	// reserving a buffer to hold the longest type
	char _buf[sizeof(largest_type<TLIST>::type)];

	// the "basic wrapper" defines the interface to be
	// implemented by each specific-type wrapper
	struct basic_type_wrapper {
		virtual void destruct(char *buf) = 0;
		virtual std::ostream & print(std::ostream &out, const char *buf) const = 0;
		virtual void clone(const char *buf_from, char *buf_to, char *buf_wrapper_to) const = 0;
		virtual const type_info & getTypeInfo(void) = 0;
	};
	// template implementation of type-wrapper
	template<typename T>
	struct specific_type_wrapper : public basic_type_wrapper {
		inline const T * getT(const char *buf) const { return reinterpret_cast<const T*>(buf); }
		inline T * getT(char *buf) { return reinterpret_cast<T*>(buf); }

		virtual void destruct(char *buf) {
			getT(buf)->~T();
		}

		virtual std::ostream & print(std::ostream &out, const char *buf) const {
			return (out << (*getT(buf)));
		}

		virtual void clone(const char *buf_from, char *buf_to, char *buf_wrapper_to) const {
			new (buf_to) T(*getT(buf_from));
			new (buf_wrapper_to) specific_type_wrapper<T>(*this);
		}

		virtual const type_info & getTypeInfo(void) {
			return typeid(T);
		}
	};

	// creating a new typelist of wrappers to all types in original TLIST
	typedef typename apply_for_each<TLIST, specific_type_wrapper>::type wraplist;
	// allocating enough space to hold the largest wrapper type
	// (currently all wrappers have the same size - that of v-table
	//  pointer, but this code is cleaner and can be extended to other
	//  formats of buffer ownership)
	char _buf_wrapper[sizeof(typename largest_type<wraplist>::type)];


public:
	/// requires default constructor to be present
	/// in the first type in the list
	variant(void) {
		new (_buf) TLIST::head();
		new (_buf_wrapper) wraplist::head(); 
	}

	/// template constructor
	template<typename T>
	variant(const T& t) {
		// verify that valid type was given - should
		// be one of listed in the TLIST
		enum { index = index_of_type<TLIST, T>::value };

		new (_buf) T(t);
		new (_buf_wrapper) specific_type_wrapper<T>();
	}


private:
	inline const basic_type_wrapper * getWrapper(void) const {
		return reinterpret_cast<const basic_type_wrapper*>(_buf_wrapper);
	}

	inline basic_type_wrapper * getWrapper(void) {
		return reinterpret_cast<basic_type_wrapper*>(_buf_wrapper);
	}

	inline void release(void) {
		getWrapper()->destruct(_buf);
	}


public:
	variant(const variant<TLIST>& v) {
		v.getWrapper()->clone(v._buf, _buf, _buf_wrapper);
	}

	friend std::ostream & operator<<(std::ostream &out, const variant<TLIST> &v) {
		return v.getWrapper()->print(out, v._buf);
	}

	~variant(void) {
		release();
	}

	variant<TLIST> & operator=(const variant<TLIST> &v) {
		release();
		v.getWrapper()->clone(v._buf, _buf, _buf_wrapper);
		return *this;
	}

	template<typename T>
	variant<TLIST> & operator=(const T &t) {
		release();
		new (_buf) T(t);
		new (_buf_wrapper) tlist_get_type<wraplist, index_of_type<TLIST, T>::value>::type();
		return *this;
	}

	template<typename T>
	T & get(void) {
		if (getWrapper()->getTypeInfo() != typeid(T))
			throw std::bad_cast("This variant does not hold requested type");
		return * reinterpret_cast<T*>(_buf);
	}

	

};

}  // end namespace